package com.snopy.lambda;

import com.snopy.pojo.Person;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.IntConsumer;
import java.util.stream.Collectors;

/**
 * @author snopy
 * @version 1.0.0
 * @classname ReductionExamples
 * @description
 * @email 77912204@qq.com
 * @date 2022/07/24 13:01
 */
public class ReductionExamples {
    public static void main(String[] args) {
        //Create sample data
        List<Person> roster = Person.createPerson(20);

        System.out.println("Contents of roster");
        roster.forEach(Person::printPerson);
        System.out.println();
        //1.Average age of male members, average operation
        double asDouble = roster
                .stream()
                .filter(p -> p.getGender() == Person.Sex.MALE)
                .mapToInt(Person::getAge)
                .average()
                .getAsDouble();

        //.ifPresent(System.out::println);
        System.out.println("Average age (bulk data operations):"+asDouble);


        //2. Sum of ages with sum operation
        int totalAge = roster.stream().mapToInt(Person::getAge).sum();
        System.out.println("Sum of ages (sum operation):"+totalAge);
        //3. Sum of ages with reduce(identify,accumulator)
        Integer totalAgeReduce = roster.stream().map(Person::getAge).reduce(0, (x, y) -> x + y);
        System.out.println("Sum of ages (reduce):"+totalAgeReduce);
        //4.Average of male members with collection operation
        double averageAge = roster
                .stream()
                .filter(p -> p.getGender() == Person.Sex.MALE)
                .map(Person::getAge)
                .collect(Averager::new, Averager::accept, Averager::combine).average();
        System.out.println("Average of male members with collection operation:"+averageAge);
        //5.Names of male members with collection operation
        System.out.println("Names of male members with collection operation:");
        List<String> namesOfMaleMembersCollect = roster.stream()
                                                       .filter(p -> p.getGender() == Person.Sex.MALE)
                                                       .map(Person::getName)
                                                       .collect(Collectors.toList());
        namesOfMaleMembersCollect.forEach(System.out::println);
        System.out.println("Group members by gender:");
        //6.Group members by gender
        Map<Person.Sex, List<Person>> collect = roster.stream().collect(Collectors.groupingBy(Person::getGender));
        ArrayList<Map.Entry<Person.Sex, List<Person>>> entries = new ArrayList<>(collect.entrySet());
        entries.forEach(e ->{
            System.out.println("Gender: "+e.getKey());
            e.getValue().stream().map(Person::getName).forEach(System.out::println);
        });
        //7.Group names by gender
        System.out.println("Group names by gender");
        Map<Person.Sex, List<String>> collect1 = roster
                .stream()
                .collect(Collectors.groupingBy(Person::getGender, Collectors.mapping(Person::getName, Collectors.toList())));
        ArrayList<Map.Entry<Person.Sex, List<String>>> entries1 = new ArrayList<>(collect1.entrySet());
        entries1.forEach(e -> {
            System.out.println("Gender: "+e.getKey());
            e.getValue().forEach(System.out::println);
        });
        //8.Total age by gender
        System.out.println("Total age by gender");
        Map<Person.Sex, Integer> collect2 = roster
                .stream()
                .collect(Collectors.groupingBy(Person::getGender, Collectors.reducing(0, Person::getAge, Integer::sum)));
        collect2.entrySet().forEach(e->{
            System.out.println("Gender: "+e.getKey()+"  Total Age:"+e.getValue());

        });
        //9.Average age by gender
        System.out.println("Average age by gender: ");
        roster
                .stream()
                .collect(Collectors.groupingBy(Person::getGender, Collectors.averagingInt(Person::getAge)))
                .forEach((key, value) -> System.out.println("Gender: " + key + "  average age :" + Math.round(value)));

        //10.
    }

}
class Averager implements IntConsumer{
    private int total = 0;
    private int count = 0;

    public Averager() {

    }

    public double average(){
        return count > 0 ? ((double) total) / count : 0;
    }
    @Override
    public void accept(int value) {
        total += value;
        count++;
    }
    //no value at all.
    public void combine(Averager other){
        total += other.total;
        count += other.count;
    }
}